/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010-2011 ProjectSkyfire <http://www.projectskyfire.org/>
 * 
 * Copyright (C) 2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORE_INSTANCE_DATA_H
#define ARKCORE_INSTANCE_DATA_H

#include "ZoneScript.h"
#include "World.h"
#include "ObjectMgr.h"
//#include "GameObject.h"
//#include "Map.h"

#define OUT_SAVE_INST_DATA             sLog->outDebug(LOG_FILTER_TSCR, "TSCR: Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_SAVE_INST_DATA_COMPLETE    sLog->outDebug(LOG_FILTER_TSCR, "TSCR: Saving Instance Data for Instance %s (Map %d, Instance Id %d) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_LOAD_INST_DATA(a)          sLog->outDebug(LOG_FILTER_TSCR, "TSCR: Loading Instance Data for Instance %s (Map %d, Instance Id %d). Input is '%s'", instance->GetMapName(), instance->GetId(), instance->GetInstanceId(), a)
#define OUT_LOAD_INST_DATA_COMPLETE    sLog->outDebug(LOG_FILTER_TSCR, "TSCR: Instance Data Load for Instance %s (Map %d, Instance Id: %d) is complete.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_LOAD_INST_DATA_FAIL        sLog->outDebug(LOG_FILTER_TSCR, "TSCR: Unable to load Instance Data for Instance %s (Map %d, Instance Id: %d).", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())

class Map;
class Unit;
class Player;
class GameObject;
class Creature;

typedef std::set<GameObject*> DoorSet;
typedef std::set<Creature*> MinionSet;

enum EncounterFrameType {
	ENCOUNTER_FRAME_ADD = 0, ENCOUNTER_FRAME_REMOVE = 1,
};

enum EncounterState {
	NOT_STARTED = 0,
	IN_PROGRESS = 1,
	FAIL = 2,
	DONE = 3,
	SPECIAL = 4,
	TO_BE_DECIDED = 5,
};

enum DoorType {
	DOOR_TYPE_ROOM = 0, // Door can open if encounter is not in progress
	DOOR_TYPE_PASSAGE = 1, // Door can open if encounter is done
	DOOR_TYPE_SPAWN_HOLE = 2, // Door can open if encounter is in progress, typically used for spawning places
	MAX_DOOR_TYPES,
};

enum BoundaryType {
	BOUNDARY_NONE = 0,
	BOUNDARY_N,
	BOUNDARY_S,
	BOUNDARY_E,
	BOUNDARY_W,
	BOUNDARY_NE,
	BOUNDARY_NW,
	BOUNDARY_SE,
	BOUNDARY_SW,
	BOUNDARY_MAX_X = BOUNDARY_N,
	BOUNDARY_MIN_X = BOUNDARY_S,
	BOUNDARY_MAX_Y = BOUNDARY_W,
	BOUNDARY_MIN_Y = BOUNDARY_E,
};

typedef std::map<BoundaryType, float> BossBoundaryMap;

struct DoorData {
	uint32 entry, bossId;
	DoorType type;
	uint32 boundary;
};

struct MinionData {
	uint32 entry, bossId;
};

struct BossInfo {
	BossInfo() :
			state(TO_BE_DECIDED) {
	}
	EncounterState state;
	DoorSet door[MAX_DOOR_TYPES];
	MinionSet minion;
	BossBoundaryMap boundary;
};

struct DoorInfo {
	explicit DoorInfo(BossInfo *_bossInfo, DoorType _type,
			BoundaryType _boundary) :
			bossInfo(_bossInfo), type(_type), boundary(_boundary) {
	}
	BossInfo *bossInfo;
	DoorType type;
	BoundaryType boundary;
};

struct MinionInfo {
	explicit MinionInfo(BossInfo *_bossInfo) :
			bossInfo(_bossInfo) {
	}
	BossInfo *bossInfo;
};

typedef std::multimap<uint32 /*entry*/, DoorInfo> DoorInfoMap;
typedef std::map<uint32 /*entry*/, MinionInfo> MinionInfoMap;

class InstanceScript: public ZoneScript {
public:

	explicit InstanceScript(Map* map) :
			instance(map), completedEncounters(0) {
	}

	virtual ~InstanceScript() {
	}

	Map *instance;

	//On creation, NOT load.
	virtual void Initialize() {
	}

	//On load
	virtual void Load(char const* data) {
		LoadBossState(data);
	}

	//When save is needed, this function generates the data
	virtual std::string GetSaveData() {
		return GetBossSaveData();
	}

	void SaveToDB();

	virtual void Update(uint32 /*diff*/) {
	}

	//Used by the map's CanEnter function.
	//This is to prevent players from entering during boss encounters.
	virtual bool IsEncounterInProgress() const;

	//Called when a player successfully enters the instance.
	virtual void OnPlayerEnter(Player* /*player*/) {
	}

	//Handle open / close objects
	//use HandleGameObject(0, boolen, GO); in OnObjectCreate in instance scripts
	//use HandleGameObject(GUID, boolen, NULL); in any other script
	void HandleGameObject(uint64 guid, bool open, GameObject* go = NULL);

	//change active state of doors or buttons
	void DoUseDoorOrButton(uint64 guid, uint32 withRestoreTime = 0,
			bool useAlternativeState = false);

	//Respawns a GO having negative spawntimesecs in gameobject-table
	void DoRespawnGameObject(uint64 guid, uint32 timeToDespawn = MINUTE);

	//sends world state update to all players in instance
	void DoUpdateWorldState(uint32 worldstateId, uint32 worldstateValue);

	// Send Notify to all players in instance
	void DoSendNotifyToInstance(char const* format, ...);

	// Complete Achievement for all players in instance
	void DoCompleteAchievement(uint32 achievement);

	// Update Achievement Criteria for all players in instance
	void DoUpdateAchievementCriteria(AchievementCriteriaTypes type,
			uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = NULL);

	// Start/Stop Timed Achievement Criteria for all players in instance
	void DoStartTimedAchievement(AchievementCriteriaTimedTypes type,
			uint32 entry);
	void DoStopTimedAchievement(AchievementCriteriaTimedTypes type,
			uint32 entry);

	// Remove Auras due to Spell on all players in instance
	void DoRemoveAurasDueToSpellOnPlayers(uint32 spell);

	// Cast spell on all players in instance
	void DoCastSpellOnPlayers(uint32 spell);

	// Return wether server allow two side groups or not
	bool ServerAllowsTwoSideGroups() {
		return sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP);
	}

	virtual bool SetBossState(uint32 id, EncounterState state);
	EncounterState GetBossState(uint32 id) const {
		return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED;
	}
	BossBoundaryMap const* GetBossBoundary(uint32 id) const {
		return id < bosses.size() ? &bosses[id].boundary : NULL;
	}

	// Achievement criteria additional requirements check
	// NOTE: not use this if same can be checked existed requirement types from AchievementCriteriaRequirementType
	virtual bool CheckAchievementCriteriaMeet(uint32 /*criteria_id*/,
			Player const* /*source*/, Unit const* /*target*/= NULL,
			uint32 /*miscvalue1*/= 0);

	// Checks boss requirements (one boss required to kill other)
	virtual bool CheckRequiredBosses(uint32 /*bossId*/,
			Player const* /*player*/= NULL) const {
		return true;
	}

	// Checks encounter state at kill/spellcast
	void UpdateEncounterState(EncounterCreditType type, uint32 creditEntry,
			Unit* source);

	// Used only during loading
	void SetCompletedEncountersMask(uint32 newMask) {
		completedEncounters = newMask;
	}

	// Returns completed encounters mask for packets
	uint32 GetCompletedEncounterMask() const {
		return completedEncounters;
	}

	void SendEncounterUnit(uint32 type, Unit* unit = NULL, uint8 param1 = 0,
			uint8 param2 = 0);

	virtual void FillInitialWorldStates(WorldPacket& /*data*/) {
	}

protected:
	void SetBossNumber(uint32 number) {
		bosses.resize(number);
	}
	void LoadDoorData(DoorData const* data);
	void LoadMinionData(MinionData const* data);

	void AddDoor(GameObject* door, bool add);
	void AddMinion(Creature* minion, bool add);

	void UpdateDoorState(GameObject *door);
	void UpdateMinionState(Creature *minion, EncounterState state);

	std::string LoadBossState(char const* data);
	std::string GetBossSaveData();
private:
	std::vector<BossInfo> bosses;
	DoorInfoMap doors;
	MinionInfoMap minions;
	uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
};
#endif
